package com.doucat.support.thread;

import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * -----------------------------------------------------------------
 * Copyright (C) 2014-2016, by your company, All rights reserved.
 * -----------------------------------------------------------------
 * File: ThreadPool.java
 * Author: wfy
 * Version: V100R001C01
 * Create: 2021/7/10 09:25
 * Changes (from 2021/1/13)
 * -----------------------------------------------------------------
 * 2021/1/13 : Create ThreadPool.java (wfy);
 * -----------------------------------------------------------------
 *
 * 1如果线程池中的线程数量未达到核心线程的数量，那么会直接启动一个核心线程来执行任务。
 *
 * 2如果线程池中的线程数量已经达到或者超过核心线程的数量，那么任务会被插入到任务队列中排队等待执行。
 *
 * 3如果在步骤2中无法再插入任务，这个时候如果未达到线程池规定的最大值，那么就会立刻启动一个非核心线程来执行任务。
 *
 * 4如果在步骤3中达到了maximumPoolSize，也将无法再插入任务，那么就拒绝执行此任务，ThreadPoolExecutor 会调用 RejectedExecutionHandler 的rejectedExecution方法来通知开发者。
 *
 * 常见的4类具有不同功能特性的线程池，它们都是直接或者间接地通过配置ThreadPoolExecutor来实现自己的功能特性
 * CachedThreadPool
 * 通过Executors的newCachedThreadPool方法来创建。
 * 它是一种线程数量不定的线程池。它只有非核心线程，并且其最大线程数量为Integer.MAX_VALUE。
 * 空闲非核心线程的超时时间为60s。这类CachedThreadPool比较适合执行大量的耗时较少的任务。
 * 缺点：
 * 最大线程数是Integer.MAX_VALUE, 并且任务队列是SynchronousQueue。
 * 也就是说这个线程池对任务来着不拒，线程不够用就创建一个， 感觉就像一个豪横的富豪。
 * 这就是问题所在了， 如果同一时刻应用的来了大量的任务，
 * 这个线程池很容易就创建过多的线程, 而创建线程又是一个很耗性能的事情， 这就容易导致应用卡顿或者直接OOM
 *
 * FixedThreadPool
 * 通过Executors的newFixedThreadPool方法来创建。
 * 它是一种线程数量固定的线程池，当线程处于空闲状态时，它们不会被回收，除非线程池被关闭。
 * 由于FixedThreadPool只有核心线程并且这些核心线程不会被回收，这意味着它能够更加快速地响应外界的请求。
 * 缺点：
 * 这个线程池到时没有上个线程池豪横了，它定死了线程数量，所以线程数量是不会超出的，
 * 但是它的任务队列是无界的LinkedBlockingQueue, 对于加进来的任务处理不过来就会存入任务队列中，
 * 并且无限制的存入队列。 这个线程池感觉就是家里有地， 无论来多少货都往里面装。
 *
 * SingleThreadExecutor
 * 通过Executors的newSingleThreadExecutor方法来创建。
 * 这类newSingleThreadExecutor线程池内部只有一个核心线程，它确保所有任务都在同一个线程中按顺序执行，不需要处理线程同步问题。
 * 缺点：
 * 这个线程池只有一个线程， 比newFixedThreadPool还穷， 但是任务队列和上面一样， 没有限制， 很容易就使用不当导致OOM
 *
 * ScheduledThreadPool
 * 通过Executors的ScheduledThreadPoolExecutor方法来创建。它的核心线程数量是固定的，而非核心线程数是没有限制的，并且非核心线程闲置时会被立即回收。这类ScheduledThreadPoolExecutor线程池主要用于执行定时任务和具有固定周期的重复任务。
 * 缺点：
 * 这个是定时任务的线程池，
 * 没有定义线程创建数量的上线，同时任务队列也没有定义上限。
 * 如果前一次定时任务还没有完成， 后一个定时任务的运行时间到了，
 * 它也会运行， 线程不够就创建。 这样如果定时任务运行的时间过长，
 * 就会导致前后两个定时任务同时执行，如果他们之间有锁，还有可能出现死锁， 此时灾难就发生了。
 *
 * 不推荐使用Executors来创建线程池
 */
public class ThreadPool {
    /**
     * 线程池的核心线程数。
     * 默认情况下，核心线程会一直在线程池中一直存活，即使处于闲置状态。
     * 除非将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true，那么闲置核心线程会在超时后终止。
     */
    private int corePoolSize;

    /**
     * 线程池所能容纳的最大线程数。
     * 当活动线程数达到这个数值后，后续新任务会被阻塞
     */
    private int maximumPoolSize;

    /**
     * 线程池中除了有核心线程之外，还有非核心线程
     * 非核心线程闲置时的超时时长，超过这个时间，非核心线程会被回收。
     * 而这个空闲的时间就是keepAliveTime
     * 当线程池线程数量大于corePoolSize时候，多出来的空闲线程，多长时间会被销毁
     */
    private long keepAliveTime;

    /**
     * 用于指定keepAliveTime参数的时间单位。
     * 常用的有TimeUnit.MILLISECONDS、TimeUnit.SECONDS和TimeUnit.MINUTES
     */
    private TimeUnit unit;

    /**
     * 线程池中非核心线程的任务队列。
     * 如果线程池中的线程数量已经达到或者超过核心线程的数量，也就是核心线程满了的话，
     * 那么通过线程池的execute方法提交的Runnable
     * 就会先尝试添加到队列中，当然未必添加成功
     * 而且队列也有多种实现，具体的后面再说，先简单理解为排队即可。
     */
    private BlockingQueue<Runnable> workQueue;

    /**
     * 线程工厂，为线程池提供创建新线程的功能。
     * threadFactory是一个接口，它只有一个方法：Thread newThread(Runnable r)
     */
    private ThreadFactory threadFactory;

    /**
     * handler 拒绝策略
     * 当线程池无法执行新的任务时
     * ThreadPoolExecutor会调用handler的rejectExecution方法
     *
     * 线程池的饱和策略，当阻塞队列满了，且没有空闲的工作线程，如果继续提交任务，必须采取一种策略处理该任务，线程池提供了4种策略
     *
     * AbortPolicy：直接抛出异常，默认策略；
     * CallerRunsPolicy：用调用者所在的线程来执行任务；
     * DiscardOldestPolicy：丢弃阻塞队列中靠最前的任务，并执行当前任务；
     * DiscardPolicy：直接丢弃任务；
     */
    private RejectedExecutionHandler handler;

    private ThreadPoolExecutor executorService;

    private volatile boolean allowCoreThreadTimeOut;
    private ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler,boolean allowCoreThreadTimeOut) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.keepAliveTime = keepAliveTime;
        this.unit = unit;
        this.workQueue = workQueue;
        this.threadFactory = threadFactory;
        this.handler = handler;
        this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
        executorService=new ThreadPoolExecutor(
                this.corePoolSize,
                this.maximumPoolSize,
                this.keepAliveTime,
                this.unit,
                this.workQueue,
                this.threadFactory,
                this.handler
        );
        /**
         * 核心线程数量，所谓核心线程就是一直保留在线程池中，
         * 及时处于空闲状态也不会销毁等线程，除非手动调用allowCoreThreadTimeOut才可以在超时销毁
         */
        executorService.allowCoreThreadTimeOut(this.allowCoreThreadTimeOut);
    }

    public void execute(Runnable runnable) {
        executorService.execute(runnable);
    }

    public Future<?> submit(Runnable runnable) {
        return executorService.submit(runnable);
    }

    /**
     * 关闭线程池，新提交的任务会被拒绝，并且尝试关闭正在执行的任务
     */
    public List<Runnable> shutdownNow() {
        return executorService.shutdownNow();
    }

    /**
     * 关闭线程池，新提交的任务会被拒绝，但是已经提交的任务会继续执行
     */
    public void shutdown() {
        executorService.shutdown();
    }

    public static class Build{
        private int corePoolSize=1;
        private int maximumPoolSize=1;
        private long keepAliveTime=30;
        private TimeUnit unit=TimeUnit.SECONDS;
        private BlockingQueue<Runnable> workQueue=new LinkedBlockingQueue<>();
        private ThreadFactory threadFactory=Executors.defaultThreadFactory();
        private RejectedExecutionHandler handler=new ThreadPoolExecutor.DiscardPolicy();
        private volatile boolean allowCoreThreadTimeOut=false;
        public Build setCorePoolSize(int corePoolSize) {
            this.corePoolSize = corePoolSize;
            return this;
        }

        public Build setMaximumPoolSize(int maximumPoolSize) {
            this.maximumPoolSize = maximumPoolSize;
            return this;
        }

        public Build setKeepAliveTime(long keepAliveTime) {
            this.keepAliveTime = keepAliveTime;
            return this;
        }

        public Build setUnit(TimeUnit unit) {
            this.unit = unit;
            return this;
        }

        public Build setWorkQueue(BlockingQueue<Runnable> workQueue) {
            this.workQueue = workQueue;
            return this;
        }

        public Build setThreadFactory(ThreadFactory threadFactory) {
            this.threadFactory = threadFactory;
            return this;
        }

        public Build setHandler(RejectedExecutionHandler handler) {
            this.handler = handler;
            return this;
        }

        public Build setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
            this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
            return this;
        }

        public ThreadPool create(){
            ThreadPool threadPool=new ThreadPool(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler,allowCoreThreadTimeOut);
            return threadPool;
        }
    }
}
